/*
 *	Name:  DoBlockModMix.h
 *
 *  Date:  10/30/08
 *
 *  Version:
 *
 *  Abstract:
 *		This file includes optimized macros and definitions to perform the SANDstorm Hash Algorithm
 *
 *  History:
 *
 *  Authors: Sandia National Laboratories
 *		Rich Schroeppel
 *		Tim Draelos
 *		Sean Malone
 *		Andrea Walker
 *		Nathan Dauhtenhan
 *		Sammy Smith
 *		Mark Torgerson
 */

/* High-level hashing macros */
#define ROT_LEFT64(x,n)		(((x)<<(n)) | ((x)>>(64-(n))))
#define ROT_LEFT128(x,n)	(((x)<<(n)) | ((x)>>(128-(n))))
#define BIT_MIX()	dtmp = (ws[0] ^ ws[2]) & J6; ws[0] ^= dtmp; ws[2] ^= dtmp; \
					dtmp = (ws[1] ^ ws[3]) & J3; ws[1] ^= dtmp; ws[3] ^= dtmp; \
					dtmp = (ws[0] ^ ws[1]) & J5; ws[0] ^= dtmp; ws[1] ^= dtmp; \
					dtmp = (ws[2] ^ ws[3]) & J5; ws[2] ^= dtmp; ws[3] ^= dtmp

#define RND1_IN(prev_block)		ws [0] ^= prev_block[0]; \
								ws [1] ^= prev_block[1]; \
								ws [2] ^= prev_block[2]; \
								ws [3] ^= prev_block[3]
#define RND_IN(prev_block, msi)	ws [0] = prev_block[0] ^ MSd [msi]; \
								ws [1] = prev_block[1] ^ MSd [msi + 1]; \
								ws [2] = prev_block[2] ^ MSd [msi + 2]; \
								ws [3] = prev_block[3] ^ MSd [msi + 3]
#define RND_OUT(prev_block, current_block)	current_block[0] = prev_block[0] ^ ws[0]; \
											current_block[1] = prev_block[1] ^ ws[1]; \
											current_block[2] = prev_block[2] ^ ws[2]; \
											current_block[3] = prev_block[3] ^ ws[3]
#define RND4_OUT(current_block)	current_block[0] = ws[0]; \
								current_block[1] = ws[1]; \
								current_block[2] = ws[2]; \
								current_block[3] = ws[3]

/* Low-level hashing macros */
#define S_CH(mi)		(MSd [mi-3] ^ (MSd [mi-1] & (MSd [mi-2] ^ MSd [mi-3])))
#define R_CH(wi)		(ws [(wi+1)&3] ^ (ws [(wi+3)&3] & (ws [(wi+2)&3] ^ ws [(wi+1)&3])))

/* Temporary variable used in low-level macros */
ULL dtmp;

#if ASM_CODE == 0

#define F64(ww)		(((ULL)((UI) ww) * (UI) ww) + ((ULL)((UI)(ww >> 32)) * ((UI)(ww >> 32))))
#define G64(mm)		(F64(mm) + ROT_LEFT64((ULL)((UI)((mm >> 32) + ACONST_256)) * ((UI)(mm + BCONST_256)), 32))
#define S_T(mi)		(MSd [mi-8] + B [mi-8] + G64(MSd [mi-1]) + S_CH(mi) + MSd [mi-4])
#define R_T(r,wi)	(ws [wi] + F64(ws [(wi+3)&3]) + R_CH(wi) + B [32-(4*r+wi)-8])

// Message Schedule macro
#define MSG_SCH(mi)	dtmp = S_T(mi); \
					MSd [mi] = ROT_LEFT64(dtmp ^ fsbox [(unsigned char)dtmp], MS_ROT_BITS)

// Round Function macro
#define MOD_MIX_RND(r,wi)	dtmp = R_T(r, wi); \
							ws [wi] = ROT_LEFT64(dtmp ^ fsbox [(unsigned char)dtmp], R_ROT_BITS)
#else
// Assembly language implementation of F64 macro
#define Fw64(fwi)	__asm	{								\
						__asm push	eax						\
						__asm push	edx						\
						__asm mov	eax, DWORD PTR fwi		\
						__asm mul	eax						\
						__asm mov	DWORD PTR dtmp, eax		\
						__asm mov	DWORD PTR dtmp+4, edx	\
						__asm mov	eax, DWORD PTR fwi+4	\
						__asm mul	eax						\
						__asm add	DWORD PTR dtmp, eax		\
						__asm adc	DWORD PTR dtmp+4, edx	\
						__asm pop	edx						\
						__asm pop	eax						\
}
#define Fm64(fmi)	__asm	{								\
						__asm push	eax						\
						__asm push	edx						\
						__asm mov	eax, DWORD PTR fmi		\
						__asm mul	eax						\
						__asm mov	DWORD PTR dtmp, eax		\
						__asm mov	DWORD PTR dtmp+4, edx	\
						__asm mov	eax, DWORD PTR fmi+4	\
						__asm mul	eax						\
						__asm add	DWORD PTR dtmp, eax		\
						__asm adc	DWORD PTR dtmp+4, edx	\
						__asm pop	edx						\
						__asm pop	eax						\
}
#define G64(mm)		(dtmp + ROT_LEFT64((ULL)((UI)((mm >> 32) + ACONST_256)) * ((UI)(mm + BCONST_256)), 32))
#define S_T(mi)		(MSd [mi-8] + B [mi-8] + G64(MSd [mi-1]) + S_CH(mi) + MSd [mi-4])
#define R_T(r,wi)	(ws [wi] + R_CH(wi) + B [32-(4*r+wi)-8])

// Message Schedule macro
#define MSG_SCH(mi)	Fm64(MSd[(mi-1)*8]); \
					dtmp = S_T(mi); \
					MSd [mi] = ROT_LEFT64(dtmp ^ fsbox [(unsigned char)dtmp], MS_ROT_BITS)
// Round Function macro
#define MOD_MIX_RND(r,wi)	Fw64(ws [((wi+3)&3)*8]); \
							dtmp += R_T(r, wi); \
							ws [wi] = ROT_LEFT64(dtmp ^ fsbox [(unsigned char)dtmp], R_ROT_BITS)
#endif
